package video

import (
	"encoding/base64"
	"fmt"
	"gitee.com/mootok/mootok/src/constant/config"
	"gitee.com/mootok/mootok/src/constant/strings"
	"gitee.com/mootok/mootok/src/models"
	"gitee.com/mootok/mootok/src/storage/database"
	"gitee.com/mootok/mootok/src/storage/file"
	"gitee.com/mootok/mootok/src/storage/redis"
	"gitee.com/mootok/mootok/src/utils/logging"
	"gitee.com/mootok/mootok/src/web/api"
	"gitee.com/mootok/mootok/src/web/common/response"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis_rate/v10"
	"github.com/sirupsen/logrus"
	"mime"
	"net/http"
	"time"
)

func (i *ApiVideoImpl) CreateUploadTask(c *gin.Context, params api.CreateUploadTaskParams) {
	var err error
	var fileName string
	var uploadToken string
	var extension string
	var uploadOptions *map[string]string = nil
	var defaultCoverUrl *string = nil

	ctxUserId, _ := c.Get(strings.UserLoginContext)
	userId := ctxUserId.(int)

	logger := logging.LogService("Video.UploadTask.Create").WithFields(logrus.Fields{
		"user_id": userId,
		"type":    params.Type,
	})

	// Rate limit
	limiter := redis_rate.NewLimiter(redis.Client)
	limiterKey := genUploadLimiterKey(userId)
	limiterRes, err := limiter.Allow(c, limiterKey, redis_rate.PerSecond(uploadMaxQPS))
	if err != nil {
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RedisOperationErrorCode, strings.InternalServiceError)
		logger.Errorf("limiter.Allow: %s", err.Error())
		return
	}
	if limiterRes.Allowed == 0 {
		logger.Errorf("ratelimit hit.")
		response.FailWithMsg(c, http.StatusInternalServerError, strings.RateLimitHitErrorCode, strings.RateLimitHit)
		return
	}

	if params.Type == "video" {
		extension = ".mp4"
		fileName = genFilename("video", userId, extension)
		defaultCoverParams := "vframe/jpg/offset/3/w/480/h/360"
		coverUrl := file.Instance.GetLink(fileName, time.Now().Unix() + int64((12 * time.Hour).Seconds()), &defaultCoverParams)
		defaultCoverUrl = &coverUrl
	} else if params.Type == "cover" {
		extension = ".jpg"
		fileName = genFilename("cover", userId, extension)
		encodedUri := base64.URLEncoding.EncodeToString([]byte(config.EnvConfig.QiNiuKodoBucket + ":" + fileName))
		persistentOps := fmt.Sprintf("imageView2/3/w/480/h/360/format/jpg/interlace/1|saveas/%s", encodedUri)
		uploadOptions = &map[string]string{
			"persistentOps": persistentOps,
		}
	} else if params.Type == "avatar" {
		//todo 地址
		extension = ".jpg"
		fileName = genFilename("avatar", userId, extension)
		encodedUri := base64.URLEncoding.EncodeToString([]byte(config.EnvConfig.QiNiuKodoBucket + ":" + fileName))
		persistentOps := fmt.Sprintf("imageView2/3/w/1024/h/768/format/jpg/interlace/1|saveas/%s", encodedUri)
		uploadOptions = &map[string]string{
			"persistentOps": persistentOps,
		}
	} else {
		logger.Errorf("invalid file type.")
		response.FailWithMsg(c, http.StatusTooManyRequests, strings.IllegalFileTypeErrorCode, strings.IllegalFileTypeError)
		return
	}
	uploadTask := models.UploadTask{
		UserId:    userId,
		FileName:  fileName,
		ExpiredAt: time.Now().Add(24 * time.Hour).Unix(),
	}

	mimeType := mime.TypeByExtension(extension)
	uploadToken = file.Instance.UploadToken(fileName,
		mimeType,
		uploadTask.ExpiredAt,
		uploadOptions,
	)

	if err = database.Client.Create(&uploadTask).Error; err != nil {
		logger.Errorf("failed save to db: %v", err)
		response.FailWithMsg(c, http.StatusInternalServerError, strings.DataBaseInsertErrorCode, strings.InternalServiceError)
		return
	}

	previewUrl := file.Instance.GetLink(fileName, time.Now().Unix() + int64((12 * time.Hour).Seconds()), nil)
	response.SuccessWithDetail(c, strings.SuccessCode, strings.Success, &api.CreateUploadTaskResp{
		Filename:    fileName,
		UploadToken: uploadToken,
		DefaultCoverUrl: defaultCoverUrl,
		PreviewUrl: previewUrl,
	})
	logger.Info("task created.")
}
